/*
 * OGLFrameworkFunction.cpp
 *
 *  Created on: 7 avr. 2011
 *      Author: seeme
 */
#include "OGLContext.h"
#include <limits>


void OGLContext::updateMatrices(GLuint program){
	Matrix4<float> mvp = m_proj * m_view * m_model;

	GLuint id = glGetUniformLocation(program, "mvp");
	glUniformMatrix4fv(id, 1, GL_TRUE, mvp.values);

	id = glGetUniformLocation(program, "model");
	glUniformMatrix4fv(id, 1, GL_TRUE, m_model.values);
}


void OGLContext::pushMatrix(){
	m_matStack.push(m_model);
}

void OGLContext::popMatrix(){
	m_model = m_matStack.top();
	m_matStack.pop();
}

void OGLContext::lookAt(GLfloat eyeX, GLfloat eyeY, GLfloat eyeZ, GLfloat targetX,
				GLfloat targetY, GLfloat targetZ, GLfloat upX, GLfloat upY, GLfloat upZ){
  GLfloat forwardX = targetX - eyeX;
  GLfloat forwardY = targetY - eyeY;
  GLfloat forwardZ = targetZ - eyeZ;

  GLfloat forwardLen = std::sqrt(forwardX * forwardX + forwardY * forwardY + forwardZ * forwardZ);
  if(forwardLen > std::numeric_limits<GLfloat>::epsilon())
  {
	forwardX /= forwardLen;
	forwardY /= forwardLen;
	forwardZ /= forwardLen;
  }

  GLfloat upLen = std::sqrt(upX * upX + upY * upY + upZ * upZ);
  if(upLen > std::numeric_limits<GLfloat>::epsilon())
  {
	upX /= upLen;
	upY /= upLen;
	upZ /= upLen;
  }

  GLfloat sX = forwardY * upZ - forwardZ * upY;
  GLfloat sY = forwardZ * upX - forwardX * upZ;
  GLfloat sZ = forwardX * upY - forwardY * upX;

  GLfloat uX = sY * forwardZ - sZ * forwardY;
  GLfloat uY = sZ * forwardX - sX * forwardZ;
  GLfloat uZ = sX * forwardY - sY * forwardX;

  m_view(0) = sX;        m_view(1) = sY;        m_view(2) = sZ;        m_view(3) = 0.0f;
  m_view(4) = uX;        m_view(5) = uY;        m_view(6) = uZ;        m_view(7) = 0.0f;
  m_view(8) = -forwardX; m_view(9) = -forwardY; m_view(10) = -forwardZ; m_view(11) = 0.0f;
  m_view(12) = 0.0f;      m_view(13) = 0.0f;      m_view(14) = 0.0f;      m_view(15) = 1.0f;

  Matrix4<float> t;
  t.setColumn(Vect4<float>(-eyeX, -eyeY, -eyeZ, 1), 3);

  m_view = m_view * t;
}

void OGLContext::setPerspective(GLfloat fovY, GLfloat ratio, GLfloat zNear, GLfloat zFar){
  GLfloat thetaY = 0.5f * (M_PI * fovY / 180.0f);
  GLfloat tanThetaY = tan(thetaY);
  GLfloat tanThetaX = tanThetaY * ratio;
  GLfloat halfW   =  tanThetaX * zNear;
  GLfloat halfH   =  tanThetaY * zNear;
  GLfloat left    = -halfW;
  GLfloat right   =  halfW;
  GLfloat bottom  = -halfH;
  GLfloat top     =  halfH;
  GLfloat iWidth  = 1.0f / (right - left);
  GLfloat iHeight = 1.0f / (top - bottom);
  GLfloat iDepth  = 1.0f / (zFar - zNear);

  m_proj(0) = 2.0f * zNear * iWidth; m_proj(1) = 0.0f;                   m_proj(2) =  (right + left) * iWidth;  m_proj(3) =  0.0f;
  m_proj(4) = 0.0f;                  m_proj(5) = 2.0f * zNear * iHeight; m_proj(6) =  (top + bottom) * iHeight; m_proj(7) =  0.0f;
  m_proj(8) = 0.0f;                  m_proj(9) = 0.0f;                   m_proj(10) = -(zFar + zNear) * iDepth;  m_proj(11) = -2.0f * (zFar * zNear) * iDepth;
  m_proj(12) = 0.0f;                  m_proj(13) = 0.0f;                   m_proj(14) = -1.0f;                     m_proj(15) =  0.0f;
}

void OGLContext::loadIdentity(){
	m_model.setIdentity();
}

void OGLContext::translate(GLfloat x, GLfloat y, GLfloat z){
  Matrix4<float> t;
  t.setColumn(Vect4<float>(x, y, z, 1), 3);
  m_model = m_model * t;
}

void OGLContext::rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z){
  GLfloat rAngle = M_PI * angle / 180.0f;
  GLfloat c = std::cos(rAngle);
  GLfloat s = std::sin(rAngle);

  GLfloat axisLen = std::sqrt(x * x + y * y + z * z);
  if(axisLen > std::numeric_limits<GLfloat>::epsilon())
  {
	x /= axisLen;
	y /= axisLen;
	z /= axisLen;
  }

		Matrix4<float> r;
	r(0) = x * x + (1.0f - x*x) * c;     r(1) = x * y * (1.0f - c) - z * s; r(2) = x * z * (1.0f - c) + y * s; r(3) = 0.0f;
	r(4) = y * x * (1.0f - c) + z * s; r(5) = y * y + (1.0f - y*y) * c;     r(6) = y * z * (1.0f - c) - x * s; r(7) = 0.0f;
	r(8) = z * x * (1.0f - c) - y * s; r(9) = y * z * (1.0f - c) + x * s; r(10) = z * z + (1.0f - z*z) * c;     r(11) = 0.0f;
	r(12) = 0.0f;                       r(13) = 0.0f;                       r(14) = 0.0f;                       r(15) = 1.0f;

 /* Matrix4<float> r;
  r(0) = x * x * (1.0f - c) + c;     r(1) = x * y * (1.0f - c) - z * s; r(2) = x * z * (1.0f - c) + y * s; r(3) = 0.0f;
  r(4) = y * x * (1.0f - c) + z * s; r(5) = y * y * (1.0f - c) + c;     r(6) = y * z * (1.0f - c) + x * s; r(7) = 0.0f;
  r(8) = z * x * (1.0f - c) - y * s; r(9) = y * z * (1.0f - c) + x * s; r(10) = z * z * (1.0f - c) + c;     r(11) = 0.0f;
  r(12) = 0.0f;                       r(13) = 0.0f;                       r(14) = 0.0f;                       r(15) = 1.0f;*/
  m_model = m_model * r;
}

void OGLContext::scale(GLfloat x, GLfloat y, GLfloat z){
  Matrix4<float> s;
  s(0) = x;    s(1) = 0.0f; s(2) = 0.0f; s(3) = 0.0f;
  s(4) = 0.0f; s(5) = y;    s(6) = 0.0f; s(7) = 0.0f;
  s(8) = 0.0f; s(9) = 0.0f; s(10) = z;    s(11) = 0.0f;
  s(12) = 0.0f; s(13) = 0.0f; s(14) = 0.0f; s(15) = 1.0f;
  m_model = m_model * s;
}

